#pragma once
#include<iostream>
#include<list>
#include<mutex>
#include<condition_variable>
#include<thread>
#include"room.hpp"
#include"online.hpp"
#include"db.hpp"
template<class T>
class mathch_queue
{
    private:
    std::list<T> _list;//因为有中间删除数据的需求，因此不使用queue
    std::mutex _mutex;
    std::condition_variable _cond;
    public:
    //阻塞线程
    void wait()
    {
        std::unique_lock<std::mutex> guard(_mutex);
        _cond.wait(guard);
    }
    int size()
    {
        std::unique_lock<std::mutex> guard(_mutex);
        return _list.size();
    }
    bool empty()
    {
        return _list.empty();
    }
    //唤醒线程
    void push(const T& val)
    {
        std::unique_lock<std::mutex> guard(_mutex);
        _list.push_back(val);
        _cond.notify_all();
    }
    bool pop(T& val)
    {
        std::unique_lock<std::mutex> guard(_mutex);
        if(_list.empty())
            return false;
        val=_list.front();
        _list.pop_front();
        return true;
    }
    //移除指定元素数据
    void remove(T& val)
    {
        std::unique_lock<std::mutex> guard(_mutex);
        _list.remove(val);
    }
};

class matcher
{
    private:
    mathch_queue<uint64_t> _q_normal;
    mathch_queue<uint64_t> _q_high;
    mathch_queue<uint64_t> _q_super;
    user_table* _user_table;
    room_manager* _room_manager;
    online_manager* _online_manager;
    std::thread _th_normal;
    std::thread _th_high;
    std::thread _th_super;
    private:
    void handle_queue(mathch_queue<uint64_t>& mq)
    {
        while(1)
        {
            //判断队列人数是否大于两个，不够则阻塞
            
            while(mq.size()<2)
            {
                //DEBUG_LOG("线程尝试等待");
                mq.wait();
            }
            //说明此时队列人数足够，取出两个
            uint64_t uid1,uid2;
            bool ret=mq.pop(uid1);
            if(ret==false)
                continue;
            ret=mq.pop(uid2);
            if(ret==false)
            {
                mq.push(uid1);
                continue;
            }
            //判断是否在线，如果有掉线，则将在线的重新加入队列
            websocket_server::connection_ptr conn1=_online_manager->get_conn_from_hall(uid1);
            if(conn1.get()==nullptr)
            {
                mq.push(uid2);
                continue;
            }
            websocket_server::connection_ptr conn2=_online_manager->get_conn_from_hall(uid2);
            if(conn2.get()==nullptr)
            {
                mq.push(uid1);
                continue;
            }
            //为两个玩家创建房间，并将玩家放入房间中
            room_ptr rm_ptr=_room_manager->create_room(uid1,uid2);
            if(rm_ptr.get()==nullptr)
            {
                mq.push(uid1);
                mq.push(uid2);
                continue;
            }
            //对两个玩家进行响应
            Json::Value resp;
            resp["optype"]="match_success";
            resp["result"]=true;
            std::string body;
            json_util::Serialize(resp,body);
            conn1->send(body);
            conn2->send(body);
        }
    }
    void thread_nomoal_entry(){return handle_queue(_q_normal);}
    void thread_high_entry(){return handle_queue(_q_high);}
    void thread_super_entry(){return handle_queue(_q_super);}
    public:
    matcher(user_table* _u_table,room_manager* _r_mane,online_manager* _on_mane)
    :_user_table(_u_table),_room_manager(_r_mane),_online_manager(_on_mane),
    _th_normal(&matcher::thread_nomoal_entry,this),
    _th_high(&matcher::thread_high_entry,this),
    _th_super(&matcher::thread_super_entry,this)
    {
        DEBUG_LOG("游戏匹配模块初始化成功");
    }
    bool add(uint64_t uid)
    {
        Json::Value user;
        bool ret=_user_table->select_by_id(uid,user);
        if(ret==false)
        {
            DEBUG_LOG("获取玩家信息失败");
            return false;
        }
        int score=user["score"].asInt();
        if(score<2000) _q_normal.push(uid);
        //else if(score<3000) _q_high.push(uid);
        //else _q_super.push(uid);
        return true;
    }
    bool del(uint64_t uid)
    {
        Json::Value user;
        bool ret=_user_table->select_by_id(uid,user);
        if(ret==false)
        {
            DEBUG_LOG("获取玩家信息失败");
            return false;
        }
        int score=user["score"].asInt();
        if(score<2000) _q_normal.remove(uid);
        //else if(score<3000) _q_high.remove(uid);
        //else _q_super.remove(uid);
        return true;
    }
};